#include "public.h"
#include "event-internal.h"
#include <sys/epoll.h>


struct event_epoll_s
{
	int			epfd;
	int			nevents;
	struct epoll_event	*events;
};

static void*    epoll_init( struct event_base_s *base, int maxevents );
static int      epoll_add( struct event_base_s *base, void *arg, struct event_s *ev );
static int      epoll_del( struct event_base_s *base, void *arg, struct event_s *ev );
static int      epoll_dispatch( struct event_base_s *base, void *arg, struct timeval *timeout );
static void     epoll_shutdown( struct event_base_s *base, void *arg );


struct  event_ops_s g_event_epoll = {
        .name           = "epoll",
        .init           = epoll_init,
        .add            = epoll_add,
        .del            = epoll_del,
        .dispatch       = epoll_dispatch,
        .shutdown       = epoll_shutdown,
};




static void*    epoll_init( struct event_base_s *base, int maxevents )
{
	struct event_epoll_s	*epoll;

	epoll = (struct event_epoll_s*)malloc(sizeof(*epoll));
	if( NULL == epoll )
		return NULL;
	
	epoll->nevents = maxevents;
	epoll->epfd = epoll_create( epoll->nevents );
	if( -1 == epoll->epfd )
	{
		log_error( "epoll_create() error %d", errno );
		free( epoll );
		return NULL;
	}

	
	epoll->events = (struct epoll_event*)malloc( epoll->nevents*sizeof(*epoll->events) );
	if( NULL == epoll->events )
	{
		close(epoll->epfd);
		free(epoll);
		return NULL;
	}
	
	return epoll;
}


static int      epoll_add( struct event_base_s *base, void *arg, struct event_s *ev )
{
	struct event_epoll_s	*epoll = ( struct event_epoll_s*)arg;
	int			op, events, nret;
	struct epoll_event	ee;

	
	op = EPOLL_CTL_ADD;
	if(ev->events&EV_READ)
		events = EPOLLIN;
	if(ev->events&EV_WRITE)
		events = EPOLLOUT;
	events |= EPOLLET;

	ee.events	= events;
	ee.data.ptr	= ev;
	nret = epoll_ctl( epoll->epfd, op, ev->fd, &ee );
	if( nret != 0 )
	{
		log_error( base->log, "epoll_ctl() fd %d op %d nret %d errno %d", 
			ev->fd, op, nret, errno );
		return -1;
	}
	
	return 0;
}



static int      epoll_del( struct event_base_s *base, void *arg, struct event_s *ev )
{
	struct event_epoll_s    *epoll = ( struct event_epoll_s*)arg;
        int                     op, events, nret;
        struct epoll_event      ee;


        op = EPOLL_CTL_DEL;
        if(ev->events&EV_READ)
                events = EPOLLIN;
        if(ev->events&EV_WRITE)
                events = EPOLLOUT;
	events |= EPOLLET;

        ee.events       = events;
        ee.data.ptr     = ev;
        nret = epoll_ctl( epoll->epfd, op, ev->fd, &ee );
        if( nret != 0 )
        {
                log_error( base->log, "epoll_ctl() fd %d op %d nret %d errno %d", 
			ev->fd, op, nret, errno );
                return -1;
        }

	return 0;
}



static int      epoll_dispatch( struct event_base_s *base, void *arg, struct timeval *timeout )
{
	struct event_epoll_s	*epoll = ( struct event_epoll_s*)arg;
	struct epoll_event	*events;
	struct event_s		*ev;
	int			i, nevents, millisec;

	millisec	= timeout->tv_sec*1000 + timeout->tv_usec/1000;  
	nevents		= epoll_wait( epoll->epfd, epoll->events, epoll->nevents, millisec );
	if( -1 == nevents )
	{
		log_error( base->log, "epoll_wait() events %P nevents %d timeout %d errno %d", 
			epoll->events, epoll->nevents, millisec, errno );
		return -1;
	}
	

	events = epoll->events;
	for( i = 0; i < nevents; i++ )
	{
		ev = (struct event_s*)events->data.ptr;

		if( (ev->events&EV_READ) && (events[i].events&EPOLLIN) )
		{
			event_base_active( base, ev, EV_READ );
		}
		
		if( (ev->events&EV_WRITE) && (events[i].events&EPOLLOUT) )
                {
                        event_base_active( base, ev, EV_WRITE );
                }
	}
	

	return 0;
}


static void     epoll_shutdown( struct event_base_s *base, void *arg )
{
	struct event_epoll_s    *epoll = ( struct event_epoll_s*)arg;

	free(epoll->events);
	close(epoll->epfd);
	free(epoll);
}
	

